package com.wrupple.muba.catalogs.client.activity.process.task.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.inject.Provider;
import com.wrupple.muba.catalogs.client.activity.process.FilterSelectionProcess;
import com.wrupple.muba.catalogs.client.activity.process.task.CatalogSelectionState;
import com.wrupple.muba.catalogs.client.module.services.logic.CatalogDescriptionService;
import com.wrupple.muba.catalogs.client.module.services.logic.FieldDescriptionService;
import com.wrupple.muba.catalogs.client.module.services.presentation.FilterableDataProvider;
import com.wrupple.muba.catalogs.client.view.BrowseView;
import com.wrupple.muba.catalogs.domain.CatalogProcessDescriptor;
import com.wrupple.muba.common.client.application.DataCallback;
import com.wrupple.muba.common.domain.FilterData;
import com.wrupple.muba.common.shared.ParallelProcess;
import com.wrupple.muba.common.shared.State;
import com.wrupple.muba.common.shared.StateTransition;
import com.wrupple.vegetate.domain.CatalogDescriptor;
import com.wrupple.vegetate.domain.FieldDescriptor;

/**
 * not actually abstract... but parametrized, realle... no?
 * 
 * @author japi
 * 
 * @param <T>
 */
public class FilterableCatalogSelectionState<T extends JavaScriptObject> extends
		SimpleSelectionState<T> implements CatalogSelectionState<T> {

	static class EmptyCreationCallback extends DataCallback<JavaScriptObject> {

		@Override
		public void execute() {
			// called IF the user decides to create a new entry when selecting
			// entries

		}

	}
	
	
	static class LoadCatalog implements State<Object, CatalogDescriptor>{

		CatalogDescriptionService service;
		String catalog;
		
		public LoadCatalog(CatalogDescriptionService service, String catalog) {
			super();
			this.service = service;
			this.catalog = catalog;
		}

		@Override
		public void start(Object parameter,
				StateTransition<CatalogDescriptor> onDone, EventBus bus) {
			service.loadCatalogDescriptor(catalog, onDone);
		}
		
	}
	
	
	class AllCatalogsLoadedCallback extends DataCallback<List<CatalogDescriptor>>{
		List<FieldDescriptor> options;
		Collection<FieldDescriptor> allFields;
		
		public AllCatalogsLoadedCallback(List<FieldDescriptor> options,
				Collection<FieldDescriptor> allFields) {
			super();
			this.options = options;
			this.allFields = allFields;
		}

		@Override
		public void execute() {
			CatalogDescriptor catalog;
			String catalogid;
			Map<String, FieldDescriptor> filterDescriptors;
			Collection<FieldDescriptor> newFields;
			for(FieldDescriptor field : allFields){
				if(field.isEphemeral()){
					catalogid = field.getForeignCatalog();
					catalog=catalogDescriptor.loadFromCache(catalogid);
					filterDescriptors = fieldDescriptor.getFilterDescriptors(catalog);
					newFields = filterDescriptors.values();
					
					for(FieldDescriptor newField : newFields){
						if(!duplicateFieldId(options,newField)){
							options.add(newField);
						}
					}
					
				}
			}
			
			initializeFilterView(options);
		}

		private boolean duplicateFieldId(List<FieldDescriptor> options,
				FieldDescriptor newField) {
			String newId = newField.getId();
			for(FieldDescriptor oldField : options){
				if(oldField.getId().equals(newId)){
					return true;
				}
			}
			return false;
		}
		
	}
	
	private class OnCatalogLoaded extends DataCallback<CatalogDescriptor>{

		@Override
		public void execute() {
			onCatalogLoaded(result);
		}
		
	}

	/*
	 * Dependencies
	 */

	private CatalogDescriptionService catalogDescriptor;
	protected FieldDescriptionService fieldDescriptor;
	
	protected BrowseView<T> view;

	private boolean allowMultipleSelection;

	private FilterData originalFilterData;
	
	protected Provider<FilterSelectionProcess> filterSelectionProcessProvider;

	public FilterableCatalogSelectionState(BrowseView<T> view,
			FilterableDataProvider<T> dataProvider, CatalogDescriptionService catalogDescriptor,FieldDescriptionService fieldDescriptor, ValueChangeHandler<FilterData> filterValueChangeHandler) {
		super(view, dataProvider);
		this.view = view;
		this.fieldDescriptor=fieldDescriptor;
		this.catalogDescriptor=catalogDescriptor;
		
		view.getFilterView().addValueChangeHandler(filterValueChangeHandler);
	}

	@Override
	public void start(final CatalogProcessDescriptor parameter,
			final StateTransition<List<T>> onDone, EventBus bus) {
		String catalogid=parameter.getSelectedType();
		catalogDescriptor.loadCatalogDescriptor(catalogid, new OnCatalogLoaded());
		super.start(parameter, onDone, bus);
		if (allowMultipleSelection) {
			view.startSelection(null, onDone);
		}
		this.originalFilterData = parameter.getFilterData();

	}

	public void onSelectionChanged(T selected) {
		// selectionModel.setSelected(selected, false);
		if (view.isSelecting()) {
			view.addToSelection(selected);
		} else {
			super.onSelectionChanged(selected);
		}
	}

	@Override
	public void allowMultipleSelection(boolean allow) {
		this.allowMultipleSelection = allow;
	}

	@Override
	public BrowseView<T> getBrowseView() {
		return view;
	}
	
	protected void onCatalogLoaded(CatalogDescriptor catalog) {
		
		Collection<FieldDescriptor> allFields = catalog.getFields();
		
		HashSet<String> catalogWeNeedToLoad = new HashSet<String>();
		for(FieldDescriptor field : allFields){
			if(field.isEphemeral()){
				catalogWeNeedToLoad.add(field.getForeignCatalog());
			}
		}
		
		Map<String, FieldDescriptor> filterDescriptors = fieldDescriptor.getFilterDescriptors(catalog);
		List<FieldDescriptor> options = new ArrayList<FieldDescriptor>(filterDescriptors.values());
		
		if(catalogWeNeedToLoad.size()>0){
			List<State<Object, CatalogDescriptor>> loadingStates = new ArrayList<State<Object, CatalogDescriptor>>(catalogWeNeedToLoad.size());
			State<Object, CatalogDescriptor> state;
			for(String catalogToLoad: catalogWeNeedToLoad){
				state = new LoadCatalog(catalogDescriptor, catalogToLoad);
				loadingStates.add(state);
			}
			ParallelProcess<Object, CatalogDescriptor> catalogLoadingProcess = new   ParallelProcess<Object, CatalogDescriptor>(loadingStates);
			StateTransition<List<CatalogDescriptor>> catalogsLoadedCallback = new AllCatalogsLoadedCallback(options,allFields);
			catalogLoadingProcess.start(null, catalogsLoadedCallback, null);
		}else{
			initializeFilterView(options);
		}
		
	}

	private void initializeFilterView(List<FieldDescriptor> options) {
		view.getFilterView().initialize(context.getProcessManager(), bus);
		if(originalFilterData==null){
			originalFilterData=new FilterData();
		}
		view.getFilterView().setValue(originalFilterData, true);
	}

}
